package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonBlockingQueue;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;


    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;


    @Autowired
    private AlbumFeignClient albumFeignClient;

    /**
     * 订单结算
     *
     * @param tradeVo
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {

        //定义价格基础变量
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        //定义订单详情集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //定义订单减免明细
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //获取用户对象信息
        UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
        Assert.notNull(userInfoVo, "用户信息为空");

        //判断
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(tradeVo.getItemType())) {//购买vip

            //获取vip套餐
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            //判断
            Assert.notNull(vipServiceConfig, "VIP套餐不存在，请联系管理员");
            //获取原价格
            originalAmount = vipServiceConfig.getPrice();
            //获取折后价
            orderAmount = vipServiceConfig.getDiscountPrice();
            //获取优惠金额 原价格-折后价格
            derateAmount = originalAmount.subtract(orderAmount);

            //封装订单详情信息
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            //设置原价格
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //封装订单减免明细

            OrderDerateVo orderDerateVo = new OrderDerateVo();
            orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
            orderDerateVo.setDerateAmount(derateAmount);
            orderDerateVo.setRemarks("VIP折扣价:" + derateAmount);
            orderDerateVoList.add(orderDerateVo);


        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(tradeVo.getItemType())) {
            //专辑购买
            //判断当前用户是否该买过该专辑
            Boolean isBuy = userFeignClient.isPaidAlbum(tradeVo.getItemId()).getData();

            //获取获取专辑信息
            if (isBuy) {
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }


            //获取专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(tradeVo.getItemId()).getData();
            Assert.notNull(albumInfo, "专辑信息为空");

            //获取原价格
            originalAmount = albumInfo.getPrice();
            //订单价格
            orderAmount = originalAmount;

            //获取用户是否为vip
            //判断用户为vip的优惠处理 isvip=1 && 没有过期
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                //判断是否有vip
                if (albumInfo.getVipDiscount().intValue() != -1) {
                    //计算优惠后的价格  100  8      aa=100*8/10  2为小数
                    orderAmount = originalAmount.multiply(albumInfo.getVipDiscount()).divide(new BigDecimal(10), 2, RoundingMode.HALF_UP);
                    //计算优惠金额
                    derateAmount = originalAmount.subtract(orderAmount);
                }


            } else {
                //普通用户处理
                if (albumInfo.getDiscount().intValue() != -1) {

                    //计算优惠后的价格  100  8      aa=100*8/10  2为小数
                    orderAmount = originalAmount.multiply(albumInfo.getDiscount()).divide(new BigDecimal(10), 2, RoundingMode.HALF_UP);
                    //计算优惠金额
                    derateAmount = originalAmount.subtract(orderAmount);

                }

            }

            //封装订单详情信息
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //封装订单优惠信息
            OrderDerateVo orderDerateVo = new OrderDerateVo();
            orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
            orderDerateVo.setDerateAmount(derateAmount);
            orderDerateVo.setRemarks("专辑优惠:" + derateAmount);
            orderDerateVoList.add(orderDerateVo);

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(tradeVo.getItemType())) {
            //声音结算--不参与折扣

            //获取待购买声音列表（已排除已购买）
            List<TrackInfo> trackInfoList = albumFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount()).getData();
            Assert.notNull(trackInfoList, "待购买列表为空");
            //获取专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            Assert.notNull(albumInfo, "专辑信息为空");

            //获取声音的价格
            BigDecimal price = albumInfo.getPrice();

            //计算订单金额 原金额
            //计算原金额
            originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
            //赋值订单金额
            orderAmount = originalAmount;

            //构造订单详情orderInfoVo

            trackInfoList.stream().forEach(trackInfo -> {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(trackInfo.getId());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemPrice(price);

                orderDetailVoList.add(orderDetailVo);


            });


        }


        //封装orderInfoVo '
        OrderInfoVo orderInfoVo = new OrderInfoVo();


        //避免订单重复提交  setnx
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //uuid
        String tradeNo = IdUtil.fastSimpleUUID();
        //存储到redis
        redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);

        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        //时间戳
        orderInfoVo.setTimestamp(DateUtil.current());

        //将orderinfoVo转换成map
        Map<String, Object> orderMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        //生成签名
        String sign = SignHelper.getSign(orderMap);

        //设置签名
        orderInfoVo.setSign(sign);

        return orderInfoVo;
    }


    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private KafkaService kafkaService;

    /**
     * 提交订单
     *
     * @param orderInfoVo
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //认证签名：防止信息被篡改
        Map<String, Object> beanToMap = BeanUtil.beanToMap(orderInfoVo);
        //排除支付payway
        beanToMap.remove("payWay");
        //根据验签工具类
        SignHelper.checkSign(beanToMap);
        //防止重复提交tradeNo
        //指定操作的key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //获取操作的value
        String tradeNo = orderInfoVo.getTradeNo();
        //判断和删除必须是原子性操作， lua脚本
        //定义lua脚本
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //创建脚本对象
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Boolean.class);

        //发送指令
        Boolean result = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), tradeNo);
        if (!result) {

            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }


        //保存订单信息  未支付 --1
        OrderInfo orderInfo = this.saveOrder(userId, orderInfoVo);


        //判断用户选择的支付方式--余额（处理）
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {

            try {
                //封装信息，调用用户账户的检查和锁定接口
                AccountLockVo accountLockVo = new AccountLockVo();
                accountLockVo.setOrderNo(orderInfo.getOrderNo());
                accountLockVo.setUserId(userId);
                accountLockVo.setAmount(orderInfoVo.getOrderAmount());
                accountLockVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());

                Result<AccountLockResultVo> resultVoResult = accountFeignClient.checkAndLock(accountLockVo);

                //判断
                if (resultVoResult.getCode().intValue() != 200 || resultVoResult.getData() == null) {

                    log.error("[订单服务]远程调用账户服务进行锁定可用余额失败:业务状态码：{},信息：{}", resultVoResult.getCode(), resultVoResult.getMessage());
                    throw new GuiguException(resultVoResult.getCode(), resultVoResult.getMessage());
                }
                //锁定成功--支付成功就是支付成功
                orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
                //修改订单状态 已支付
                orderInfoMapper.updateById(orderInfo);

                //发送消息扣除用户金额 2
                kafkaService.sendMessage(KafkaConstant.QUEUE_ACCOUNT_MINUS, orderInfo.getOrderNo());

                //发送消息记录购买记录
                //更新用户的购买记录 vip 专辑 声音
                UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
                userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
                userPaidRecordVo.setUserId(userId);
                userPaidRecordVo.setItemType(orderInfoVo.getItemType());
                //获取详情中所有的id
                List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(orderDetailVo -> orderDetailVo.getItemId()).collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(itemIdList);
                //发送消息-- 3
                kafkaService.sendMessage(KafkaConstant.QUEUE_USER_PAY_RECORD, JSON.toJSONString(userPaidRecordVo));

            } catch (GuiguException e) {
                //发送消息
                //出现异常，进行回滚，锁定金额释放 4
                kafkaService.sendMessage(KafkaConstant.QUEUE_ACCOUNT_UNLOCK, orderInfo.getOrderNo());

                //抛出异常
                throw new RuntimeException(e.getMessage());
            }

        }



        //处理返回信息
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("orderNo", orderInfo.getOrderNo());


        //发送延迟消息--延迟关闭订单
        this.sendDelayMessage(orderInfo.getId());

        return resultMap;
    }


    @Autowired
    private RedissonClient redissonClient;
    /**
     * 发送延迟消息关闭订单
     * @param id
     */
    private void sendDelayMessage(Long orderId) {
        try {
            //创建阻塞队列
            RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(KafkaConstant.QUEUE_ORDER_CANCEL);
            //创建延迟队列
            RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
            //发送消息
//            delayedQueue.offer(String.valueOf(orderId),KafkaConstant.DELAY_TEMP_TIME,TimeUnit.SECONDS);
            delayedQueue.offer(String.valueOf(orderId),KafkaConstant.DELAY_TIME,TimeUnit.SECONDS);
            log.info("延迟消息发送成功：{}",orderId);
        } catch (Exception e) {
            log.error("延迟消息发送失败：{}",e);
           e.printStackTrace();
        }


    }

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    /**
     * 保存订单信息
     *
     * @param userId
     * @param orderInfoVo
     * @return order_info 订单信息表
     * order_detail 订单详情表
     * order_derate 订单减免表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderInfo saveOrder(Long userId, OrderInfoVo orderInfoVo) {

        //保存order_info
        //copy OrderInfoVo中的数据 OrderInfo
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //设置用户id
        orderInfo.setUserId(userId);
        //设置orderNo YYYYMMdd+分布式id（雪花算法）
        String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextIdStr();
        orderInfo.setOrderNo(orderNo);
        //设置订单标题
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        //设置订单状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //保存
        orderInfoMapper.insert(orderInfo);

        //获取orderId
        Long orderId = orderInfo.getId();


        //保存order_detail
        orderInfoVo.getOrderDetailVoList().forEach(orderDetailVo -> {

            //拷贝对应数据
            OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
            orderDetail.setOrderId(orderId);
            orderDetailMapper.insert(orderDetail);

        });


        //保存order_derate

        orderInfoVo.getOrderDerateVoList().forEach(orderDerateVo -> {
            OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
            //设置orderId
            orderDerate.setOrderId(orderId);
            orderDerateMapper.insert(orderDerate);

        });

        return orderInfo;
    }


    /**
     * 根据订单号获取订单信息
     * @param userId
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(Long userId, String orderNo) {

        //select *from order_info where user_id =? and order_no=?
        LambdaQueryWrapper<OrderInfo> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getUserId,userId);
        queryWrapper.eq(OrderInfo::getOrderNo,orderNo);
        //查询
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

        //转换订单状态
        orderInfo.setOrderStatusName(this.getOrderStatusName(orderInfo.getOrderStatus()));


        //查询订单详情列表
        //select*from order_detail where order_id=?
        LambdaQueryWrapper<OrderDetail> orderDetailQueryWrapper=new LambdaQueryWrapper<>();
        orderDetailQueryWrapper.eq(OrderDetail::getOrderId,orderInfo.getId());
        //查询
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailQueryWrapper);
        //封装
        orderInfo.setOrderDetailList(orderDetails);

        //查询订单优惠列表
        LambdaQueryWrapper<OrderDerate> derateQueryWrapper =new LambdaQueryWrapper<>();
        derateQueryWrapper.eq(OrderDerate::getOrderId,orderInfo.getId());
        //查询
        List<OrderDerate> orderDerates = orderDerateMapper.selectList(derateQueryWrapper);
        orderInfo.setOrderDerateList(orderDerates);


        //设置支付方式（文字）
        //获取支付方式
        String payWay = orderInfo.getPayWay();
        //转换
        String payWayName=this.getPayWayName(payWay);
        orderInfo.setPayWayName(payWayName);

        return orderInfo;
    }

    /**
     * 我的订单列表
     * @param orderInfoPage
     * @param userId
     * @return
     */
    @Override
    public Page<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage, Long userId) {

        Page<OrderInfo> infoPage=orderInfoMapper.findUserPage(orderInfoPage,userId);

        //转换两个参数 支付方式 订单状态
        infoPage.getRecords().forEach(orderInfo -> {

            String orderStatusName = this.getOrderStatusName(orderInfo.getOrderStatus());
            String payWayName = this.getPayWayName(orderInfo.getPayWay());
            orderInfo.setOrderStatusName(orderStatusName);
            orderInfo.setPayWayName(payWayName);
        });



        return infoPage;
    }

    /**
     *  关闭订单
     * @param orderId
     */
    @Override
    public void orderCancal(String orderId) {

        //根据orderId查询订单
        OrderInfo orderInfo = orderInfoMapper.selectById(Long.valueOf(orderId));
        //判断
        if(orderInfo!=null&&orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)){
            //判断是否为未支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            //修改
            orderInfoMapper.updateById(orderInfo);


        }





    }

    /**
     * 转换订单状态
     * @param orderStatus
     * @return
     */
    private String getOrderStatusName(String orderStatus) {
        //判断
        if(SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)){

            return "未支付";
        }else if(SystemConstant.ORDER_STATUS_PAID.equals(orderStatus)){


            return "已支付";
        }else if(SystemConstant.ORDER_STATUS_CANCEL.equals(orderStatus)){

            return "已取消";
        }

        return "";

    }

    /**
     * 转换支付方式提示
     * @param payWay
     * @return
     */
    private String getPayWayName(String payWay) {
        //判断
        if(SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)){

            return "微信";
        }else if(SystemConstant.ORDER_PAY_WAY_ALIPAY.equals(payWay)){


            return "支付宝";
        }else if(SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)){

            return "余额";
        }

        return "";


    }


}
